Expose isFinished() on AgentSpan#11884
Conversation
Add a non-breaking default boolean isFinished() to the AgentSpan interface (internal-api), surfacing the finished state DDSpan already tracks (durationNano != 0) so other modules can query it without reaching into dd-trace-core. DDSpan's existing method overrides the default; the TrackingSpanDecorator test wrapper delegates; leaf/synthetic spans (NoopSpan etc.) keep the false default. Adds a DDSpanTest case covering false-before / true-after-finish via the interface. Motivated by the FFE span-enrichment interceptor (PR #11658), which needs to distinguish a truly-finished local root from a still-running one (long-running traces) and resolve it in O(1); that consumer lands in a separate follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Hi! 👋 Thanks for your pull request! 🎉 To help us review it, please make sure to:
If you need help, please check our contributing guidelines. |
🟢 Java Benchmark SLOs — All performance SLOs passed
PR vs. master results
Commit: Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion. |
There was a problem hiding this comment.
Pull request overview
Adds a non-breaking isFinished() default method to the AgentSpan interface so modules depending on internal-api can query span completion without reaching into dd-trace-core, while preserving existing behavior for DDSpan.
Changes:
- Introduces
default boolean isFinished()onAgentSpan(defaultfalse). - Adds a
DDSpanTestcase assertingisFinished()transitions fromfalsetotrueafterfinish(), via theAgentSpaninterface. - Updates
TrackingSpanDecorator(test wrapper) to delegateisFinished()to the wrapped span.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/AgentSpan.java | Adds isFinished() as a default method on the core span interface. |
| dd-trace-core/src/test/java/datadog/trace/core/DDSpanTest.java | Adds coverage to validate AgentSpan.isFinished() reflects DDSpan completion. |
| dd-java-agent/instrumentation-testing/src/main/groovy/datadog/trace/agent/test/TrackingSpanDecorator.groovy | Delegates isFinished() in the AgentSpan test wrapper. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
🎯 Code Coverage (details) 🔗 Commit SHA: 9f5d776 | Docs | Datadog PR Page | Give us feedback! |
AlexeyKuznetsov-DD
left a comment
There was a problem hiding this comment.
LGTM.
It would probably be worth getting one more approval from an experienced reviewer before merging.
| * @return {@code true} once this span has finished (its duration is set). Implementations that do | ||
| * not track finish state always return {@code false}. | ||
| */ | ||
| default boolean isFinished() { |
There was a problem hiding this comment.
Would it make more sense to not have this default to false? i.e. just boolean isFinished();
It looks like the classes that implement AgentSpan, are TrackingSpanDecorator (addressed in PR already), DDSpan (already has isFinished() defined, so just needs an @Override), and ImmutableSpan (would need implementation). This would make behavior explicit per span implementation.
sarahchen6
left a comment
There was a problem hiding this comment.
LGTM, but cc @PerfectSlayer to confirm internal-api changes are okay
PerfectSlayer
left a comment
There was a problem hiding this comment.
Evaluating a remote span as always finished seem wrong.
But I would like to get back at the motivation first because it's unclear why it's needed in the first place:
The FFE span-enrichment interceptor (#11658) needs to tell a truly-finished local-root span from a still-running one
That tells it needs it but not why, right?
— with long-running traces enabled, a periodic flush emits the still-running root, and the interceptor currently mis-reads "root present in the flushed batch" as "trace finished".
Sure but that's the principle of the interceptor. By the way, we want to get rid of it - that mean we will introduce some other capabilities as alternatives.
It also wants to resolve the root in O(1) instead of scanning the batch.
Sure, but why?
Both need a finished signal on the interface it uses (AgentSpan).
Who's both?
DDSpan already has it, but the interface didn't expose it; this closes only that gap.
Yes, it's sort of expected to hide the implementation details. Even the OTel span API would need it implemented its own logic rather than exposing the wrapped DDSpan internals.
So, back to the original question, what's the need of getting the state of the span? Why do you need to figure out if it's finished or not? And by the way, the direction the next API would go is, if a span is finished, it will be immutable.
|
Thanks @PerfectSlayer — you comment is really helpful. That said, your points land: if the interceptor mechanism is going away with an alternative coming, adding Since #11658 is approved and doesn't depend on this (the long-running case is off-by-default and currently a documented known limitation), I'm happy to not pursue this. I'd propose parking #11884 and revisiting the FFE long-running fix when the interceptor-replacement capability lands — unless you'd prefer we use a different signal now, in which case what would you suggest? |
What Does This Do
Adds a non-breaking
default boolean isFinished()to theAgentSpaninterface (internal-api), surfacing the finished state thatDDSpanalready tracks (durationNano != 0) so other modules can query it without reaching intodd-trace-core.DDSpan's existingisFinished()overrides the default — no behavior change.TrackingSpanDecoratortest wrapper (the onlyAgentSpanwrapper) delegates to its wrapped span.NoopSpan, etc.) keep thefalsedefault.DDSpanTestcase assertingfalsebefore finish /trueafter, exercised through theAgentSpaninterface.Motivation
The FFE span-enrichment interceptor (#11658) needs to tell a truly-finished local-root span from a still-running one — with long-running traces enabled, a periodic flush emits the still-running root, and the interceptor currently mis-reads "root present in the flushed batch" as "trace finished". It also wants to resolve the root in O(1) instead of scanning the batch. Both need a finished signal on the interface it uses (
AgentSpan).DDSpanalready has it, but the interface didn't expose it; this closes only that gap. The consumer change (the interceptor rewrite) lands as a separate follow-up on #11658.Additional Notes
defaultmethod, so no existingAgentSpanimplementer has to change. Making it abstract would break every implementer.DDSpanalready returns this value.MutableSpan/dd-trace-api(the FFE interceptor casts toAgentSpan).Contributor Checklist
type:and (comp:orinst:) labels in addition to any other useful labelsclose,fix, or any linking keywords when referencing an issueUse
solvesinstead, and assign the PR milestone to the issue/merge. You can also:/merge --commit-message "..."/merge -c/merge -f --reason "reason"; please use this judiciously, as some checks do not run at the PR-level (note: the PR still needs to be mergeable, this will only skip the pre-merge build)